Controllers and callbacks topic

Controllers & Callbacks

This is part of the kalender documentation.

Controllers drive the calendar from your code. Callbacks report back what the user did. Together they are how the calendar connects to the rest of your app.


Controllers

EventsController

EventsController manages and exposes events to the calendar. Typically one instance per app. Use DefaultEventsController unless you need a custom storage layer.

Its methods are addEvent, addEvents, removeEvent, removeEvents, removeWhere, removeById, updateEvent, replaceEvents, byId, clearEvents and eventsInRange.

eventsInRange takes a FloatingDateTimeRange, not a KalenderDateTimeRange. Convert with FloatingDateTimeRange.fromDateTimeRange(range).

KalenderController

KalenderController drives the KalenderView widgets built on it. It holds the viewConfiguration and location. Setting either switches the view, see Switching between views and Location.

State notifiers:

Notifier Type Description
visibleDateTimeRange ValueListenable<KalenderDateTimeRange> The currently visible date range
floatingVisibleRange ValueListenable<FloatingDateTimeRange> The same range without a timezone
visibleTimeOfDay ValueListenable<KalenderTime?> Time aligned with the top of the viewport (multi-day views, null otherwise)
visibleEvents ValueListenable<Set<KalenderEvent>> Events on the page on screen
selectedEvent ValueNotifier<KalenderEvent?> The focused event (shows drop target / resize handles)
selectedRange ValueNotifier<FloatingDateTimeRange?> The selected days, ending at midnight after the last one
openDayOverlay ValueNotifier<FloatingDateTime?> The day whose overlay is open (month view and multi-day header, null otherwise)

Navigation methods:

  • jumpToPage(page) / jumpToDate(date)
  • animateToNextPage() / animateToPreviousPage()
  • animateToDate(date) / animateToDateTime(dateTime)
  • animateToEvent(event)

Selection methods: selectEvent(event) focuses an event from code, which is what draws its drop target and resize handles. deselectEvent() clears it. Both drive the selectedEvent notifier above. selectDate(date) and selectRange(range) select whole days, deselectRange() clears them and isDateSelected(date) tests one. They drive selectedRange. Pass navigate: true to move the view to a selection that is off screen.

Day overlay: showDayOverlay(date) opens the overlay listing a day's events in the month view and the multi-day header, and hideDayOverlay() closes it. Both drive openDayOverlay. A day off screen opens nothing unless navigate: true is passed.

Disposing

Both controllers hold listeners, so dispose them with the widget that owns them:

kalenderController.dispose();
eventsController.dispose();

An EventsController shared across screens belongs to whatever owns it for the life of the app, and is disposed there rather than in a single screen.

Building the surrounding UI

The calendar draws no toolbar of its own. Switching views, moving between pages and showing the current month are all built in your app, using the navigation methods above and the controller's viewConfiguration.

class CalendarScreen extends StatefulWidget {
  const CalendarScreen({super.key});

  @override
  State<CalendarScreen> createState() => _CalendarScreenState();
}

class _CalendarScreenState extends State<CalendarScreen> {
  final viewConfigurations = <ViewConfiguration>[
    MultiDayViewConfiguration.week(),
    MonthViewConfiguration.singleMonth(),
  ];
  late final kalenderController = KalenderController(viewConfiguration: viewConfigurations.first);

  @override
  void dispose() {
    kalenderController.dispose();
    super.dispose();
  }

  @override
  Widget build(BuildContext context) {
    return Column(
      children: [
        Row(
          children: [
            ValueListenableBuilder(
              valueListenable: kalenderController.floatingVisibleRange,
              builder: (context, range, child) {
                final month = range.dominantMonthDate;
                return Text('${month.monthNameLocalized()} ${month.year}');
              },
            ),
            IconButton(onPressed: kalenderController.animateToPreviousPage, icon: const Icon(Icons.chevron_left)),
            IconButton(onPressed: kalenderController.animateToNextPage, icon: const Icon(Icons.chevron_right)),
            ListenableBuilder(
              listenable: kalenderController,
              builder: (context, child) => DropdownButton<ViewConfiguration>(
                value: kalenderController.viewConfiguration,
                items: [for (final c in viewConfigurations) DropdownMenuItem(value: c, child: Text(c.name))],
                onChanged: (value) => kalenderController.viewConfiguration = value!,
              ),
            ),
          ],
        ),
        Expanded(
          child: KalenderView(
            eventsController: eventsController,
            kalenderController: kalenderController,
          ),
        ),
      ],
    );
  }
}

Setting kalenderController.viewConfiguration is all a view change takes. What carries over, such as the date and scroll position, is set on the configuration itself, see Views.

The basic example has a fuller version of this toolbar.


Callbacks

Pass a KalenderCallbacks to KalenderView to react to user interactions.

KalenderCallbacks(
  // --- Event interactions ---

  // Called when an event tile is tapped.
  onEventTapped: (event) {},

  // With tap position and the tile's RenderBox.
  onEventTappedWithDetail: (event, detail) {},

  // Called when an event is secondary tapped (right-clicked).
  onEventSecondaryTapped: (event) {},
  onEventSecondaryTappedWithDetail: (event, detail) {},

  // Called before the calendar creates a new event from a gesture.
  // Return your concrete Event subclass here.
  onEventCreate: (event) {
    return Event(start: event.start,
      end: event.end, title: 'New Event');
  },

  // onEventCreateWithDetail: (event, detail) {...} also receives the gesture
  // detail, and is used instead of onEventCreate when set.

  // Called after a new event has been committed. Add it to your controller here.
  onEventCreated: (event) => eventsController.addEvent(event),

  // Called just before a rescheduled / resized event is applied.
  onEventChange: (event) {},

  // Called after a rescheduled / resized event is applied.
  onEventChanged: (original, updated) {
    eventsController.updateEvent(event: original, updatedEvent: updated);
  },

  // --- Calendar interactions ---

  // Called when the visible page changes.
  onPageChanged: (visibleDateTimeRange) {},

  // Called when the vertical scroll position of a multi-day view changes.
  // 'visibleTimeOfDay' is the time aligned with the top of the viewport.
  onScrollPositionChanged: (visibleTimeOfDay) {},

  // Called when the user taps an empty area (day / week body, month cell,
  // empty schedule day).
  onTapped: (date) {},
  onTappedWithDetail: (detail) {
    // detail.dateTime or detail.dateTimeRange, plus renderBox & localOffset.
  },

  // Called when the user secondary taps (right-clicks) an empty area.
  onSecondaryTapped: (date) {},
  onSecondaryTappedWithDetail: (detail) {},

  // Called when the user long-presses an empty area.
  onLongPressed: (date) {},
  onLongPressedWithDetail: (detail) {},

  // Called when the user secondary long-presses an empty area.
  onSecondaryLongPressed: (date) {},
  onSecondaryLongPressedWithDetail: (detail) {},

  // Taps, secondary taps and long presses on a date label (day number, day
  // name, schedule date) and on a week number. Each listens only for what is set.
  dateLabel: GestureCallbacks(onTap: (detail) {}),
  weekNumber: GestureCallbacks(onTap: (detail) {}),

  // --- Drag-and-drop acceptance ---

  // Day / week vertical drag target. Return false to reject the drop.
  onWillAcceptWithDetailsVertical: (details, controller, configuration) => true,

  // Month / header horizontal drag target.
  onWillAcceptWithDetailsHorizontal: (details, controller, configuration) => true,
)

Classes

ContinuousScheduleViewController Controllers and callbacks
The ScheduleViewController of ScheduleViewConfiguration.continuous, one list over the display range.
DayDetail Controllers and callbacks
The detail for when the calendar is tapped.
GestureCallbacks<T extends TapDetail> Controllers and callbacks
The gestures reported for one part of the calendar, such as KalenderCallbacks.dateLabel.
KalenderCallbacks Controllers and callbacks
The callbacks used by the KalenderView.
KalenderController Controllers and callbacks
Holds the ViewConfiguration and Location of a calendar and the ViewController a KalenderView shows.
KalenderScope Controllers and callbacks
Reads the state of the KalenderView a widget is built inside.
MonthViewController Controllers and callbacks
The controller of a month view. It opens on the month of the date of initial.
MultiDayDetail Controllers and callbacks
The detail for when a multi-day range is tapped.
MultiDayViewController Controllers and callbacks
The controller of a multi-day view. It opens on the date, time of day and zoom of initial.
PaginatedScheduleViewController Controllers and callbacks
The ScheduleViewController of ScheduleViewConfiguration.paginated, one page per month.
ScheduleViewController Controllers and callbacks
The controller of a schedule view. It opens on the date of initial.
TapDetail Controllers and callbacks
The detail of a gesture on the calendar, a DayDetail or a MultiDayDetail depending on the calendar view.
ViewController Controllers and callbacks
A controller for calendar views.

Typedefs

OnEventChange = void Function(KalenderEvent event) Controllers and callbacks
The callback for when an event is about to be changed.
OnEventChanged = void Function(KalenderEvent event, KalenderEvent updatedEvent) Controllers and callbacks
The callback for when an event is changed.
OnEventCreate = KalenderEvent? Function(KalenderEvent event) Controllers and callbacks
The call back for creating a new event.
OnEventCreated = void Function(KalenderEvent event) Controllers and callbacks
The callback for a new event has been created.
OnEventCreateWithDetail = KalenderEvent? Function(KalenderEvent event, TapDetail detail) Controllers and callbacks
The call back for creating a new event with details.
OnEventTapped = void Function(KalenderEvent event) Controllers and callbacks
The callback for when an event is tapped.
OnEventTappedWithDetail = void Function(KalenderEvent event, TapDetail detail) Controllers and callbacks
The callback for when an event is tapped.
OnGesture<T extends TapDetail> = void Function(T detail) Controllers and callbacks
A callback for a gesture on one part of the calendar.
OnLongPressed = void Function(DateTime date) Controllers and callbacks
The callback for when a user long presses on an empty space in the calendar.
OnLongPressedWithDetail = void Function(TapDetail detail) Controllers and callbacks
The callback for when a user long presses on an empty space in the calendar with details.
OnPageChanged = void Function(KalenderDateTimeRange dateTimeRange) Controllers and callbacks
The callback for when a calendar page is changed.
OnScrollPositionChanged = void Function(KalenderTime visibleTimeOfDay) Controllers and callbacks
The callback for when the vertical scroll position of a multi-day view changes.
OnTapped = void Function(DateTime date) Controllers and callbacks
The callback for when a user taps on an empty space in the calendar.
OnTappedWithDetail = void Function(TapDetail detail) Controllers and callbacks
The callback for when a user taps on an empty space in the calendar with details.